package com.yuantong.xyerp.rest.controller.base;

import apijson.JSON;
import apijson.framework.APIJSONController;
import apijson.orm.Parser;
import com.alibaba.fastjson.JSONObject;
import com.yuantong.xyerp.core.api.common.Constant;
import com.yuantong.xyerp.core.api.common.MenuCode;
import com.yuantong.xyerp.core.api.dto.UserDto;
import com.yuantong.xyerp.core.api.exception.YuantongException;
import com.yuantong.xyerp.core.api.model.UserDetail;
import com.yuantong.xyerp.core.api.service.IUserService;
import com.yuantong.xyerp.core.api.util.AesCipherUtil;
import com.yuantong.xyerp.rest.config.shiro.jwt.JwtUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.Objects;

/**
 * Created by Jianguo.Yin on 2021-07-07.
 */
@Slf4j
@Api(tags = "APIJSON抽象接口")
@RestController
@RequestMapping("")
public class ApiJsonController extends APIJSONController {

    @Autowired
    private IUserService userService;
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Value("${dataRights.enable}")
    private Boolean enable;


    @Override
    public Parser<Long> newParser(HttpSession session, apijson.RequestMethod method) {
        return super.newParser(session, method).setNeedVerify(false); //TODO 这里关闭校验，方便新手快速测试，实际线上项目建议开启
    }

    /**
     * 获取
     */
    @PostMapping(value = "/get", produces = "application/json; charset=utf-8")
    @ApiOperation(value = "APIJSON:/get", httpMethod = "POST",notes = "APIJSON接口")
    public String get(@RequestBody String request, HttpServletRequest httpServletRequest, HttpSession session) {
        if (enable){
            log.info("request初: {}", request);
            String menuCode = getMenuCode(httpServletRequest);
            if (menuCode == null)
                throw new YuantongException("0100","menuCode为空");
            log.info("(get)menuCode:{}",menuCode);

            switch (menuCode) {
                case MenuCode.sales_list_menu:
                case MenuCode.purchase_list_menu:
                    request = this.addClause(request, httpServletRequest, "Contract", Constant.Clause_Sign_By, true, Constant.Clause_Creator_Id);
                    break;
                case MenuCode.partener_supplyer:
                case MenuCode.partener_client:
                case MenuCode.partener_agent:
                    request = this.addClause(request, httpServletRequest, "Merchant", Constant.Clause_Operator_Id, false, null);
                    break;
                case MenuCode.return_list:
                    request = this.addClause(request, httpServletRequest, "", Constant.Clause_Creator_Id, true, Constant.Clause_Operator_Id);
                    break;
                case MenuCode.tranport_receipt:
                    request = this.addClause(request, httpServletRequest, "ContractTransportation", Constant.Clause_Creator_Id, false, null);
                    break;
                case MenuCode.warehouse_checking: // 库存盘点
                    request = this.addClause(request, httpServletRequest, "Inventory", Constant.Clause_Creator_Id, false, null);
                    break;
                case MenuCode.warehouse_transfer:
                    request = this.addClause(request, httpServletRequest, "Allocation", Constant.Clause_Creator_Id, true, Constant.Clause_Operator_Id);
                    break;
                case MenuCode.warehouse_rawCheck: // 入库质检
                    request = this.addClause(request, httpServletRequest, "QualityCheckReport", Constant.Clause_Creator_Id, false, null);
                    break;
                case MenuCode.warehouse_stockIn:
                case MenuCode.warehouse_stockOut:
                    request = this.addClause(request, httpServletRequest, "WarehouseReceipt", Constant.Clause_Creator_Id, false, null);
                    break;
                case MenuCode.contract_sale:
                    request = this.addClause(request, httpServletRequest, "", Constant.Clause_Sign_By, true, Constant.Clause_Creator_Id);
                    break;
                case MenuCode.contract_purchase:
                    request = this.addClause(request, httpServletRequest, "", Constant.Clause_Sign_By, true, Constant.Clause_Creator_Id);
                    break;
                case MenuCode.finance_contractReceipt:
                    request = this.addClause(request, httpServletRequest, "", Constant.Clause_Sign_By, true, Constant.Clause_Creator_Id);
                    break;
                case MenuCode.finance_receiptList:
                    request = this.addClause(request, httpServletRequest, "", Constant.Clause_Creator_Id, true, Constant.Clause_Operator_Id);
                    break;
                case MenuCode.finance_contractPayment:
                    request = this.addClause(request, httpServletRequest, "", Constant.Clause_Sign_By, true, Constant.Clause_Creator_Id);
                    break;
                case MenuCode.finance_paymentList:
                    request = this.addClause(request, httpServletRequest, "", Constant.Clause_Creator_Id, true, Constant.Clause_Operator_Id);
                    break;
                case MenuCode.finance_contractInvoice:
                    request = this.addClause(request, httpServletRequest, "", Constant.Clause_Sign_By, true, Constant.Clause_Creator_Id);
                    break;
                case MenuCode.finance_invoiceList:
                    request = this.addClause(request, httpServletRequest, "", Constant.Clause_Creator_Id, false, null);
                    break;
                case MenuCode.finance_transportationFeePay:
                    request = this.addClause(request, httpServletRequest, "", Constant.Clause_Creator_Id, false, null);
                    break;
            }
        }
        return super.get(request, session);
    }

    /**
     * 计数
     */
    @Override
    @PostMapping(value = "/head", produces = "application/json")
    @ApiOperation(value = "APIJSON:/head", httpMethod = "POST",notes = "APIJSON接口")
    public String head(@RequestBody String request, HttpSession session) {
        return super.head(request, session);
    }

    /**
     * 新增
     */
    @PostMapping(value = "/post",produces = "application/json")
    @ApiOperation(value = "APIJSON:/post", httpMethod = "POST",notes = "APIJSON接口")
    public String post(@RequestBody String request, HttpSession session, HttpServletRequest httpServletRequest) {
        // menuCode 得到具体业务
        String menuCode = getMenuCode(httpServletRequest);
        JSONObject apiJsonRequest = JSON.parseObject(request);
        if (Objects.equals(MenuCode.basic_user,menuCode)){
            JSONObject userJsonObject = apiJsonRequest.getJSONObject("User");
            userJsonObject.put("password", AesCipherUtil.enCrypto(userJsonObject.getString("password")));
            request = apiJsonRequest.toString();
        }
        return super.post(request, session);
    }

    /**
     * 修改
     */
    @PostMapping(value = "/put",produces = "application/json")
    @ApiOperation(value = "APIJSON:/put", httpMethod = "POST",notes = "APIJSON接口")
    public String put(@RequestBody String request, HttpSession session, HttpServletRequest httpServletRequest) {
        String menuCode = getMenuCode(httpServletRequest);
        // TODO: 为完成, 应该把if 分开写的, 明天再xie
        /** 修改 员工 部门 角色 都需要删除旧缓存, 但是无法查询到新数据添加到缓存, 因为此时apijson更新还没完成 */
        if (Objects.equals(MenuCode.basic_user,menuCode) || Objects.equals(MenuCode.basic_role,menuCode) || Objects.equals(MenuCode.basic_deparment,menuCode)){
            log.info("put中的menuCode:{}",menuCode);
            // 如果 修改的 用户, 则更新该loginName的缓存
            // 如果 修改的是角色, 则更新所有该角色拥有的用户loginNameS 缓存到期
            // 部门 到期
            JSONObject apiJsonRequest = apijson.JSONObject.parseObject(request);
            JSONObject mainJson;
            if (apiJsonRequest.containsKey("User")){
                mainJson = apiJsonRequest.getJSONObject("User");
                String targetLoginName = (String) mainJson.get("loginName");
                if (Boolean.TRUE.equals(redisTemplate.hasKey(Constant.PREFIX_USER_CACHE + targetLoginName)))
                    redisTemplate.delete(Constant.PREFIX_USER_CACHE + targetLoginName);
            }
            if (apiJsonRequest.containsKey("Role")){
                mainJson = apiJsonRequest.getJSONObject("Role");
                List<String> targetLoginNames = userService.getLoginNameByRoleId(new UserDto().setRoleId((String) mainJson.get("id")));
                for (String targetLoginName :
                        targetLoginNames) {
                    if (Boolean.TRUE.equals(redisTemplate.hasKey(Constant.PREFIX_USER_CACHE + targetLoginName)))
                        redisTemplate.delete(Constant.PREFIX_USER_CACHE + targetLoginName);
                }
            }
            if (apiJsonRequest.containsKey("Organization")){
                mainJson = apiJsonRequest.getJSONObject("Organization");
                List<String> targetLoginNames = userService.getLoginNameByRoleId(new UserDto().setRoleId((String) mainJson.get("id")));
                for (String targetLoginName : targetLoginNames) {
                    if (Boolean.TRUE.equals(redisTemplate.hasKey(Constant.PREFIX_USER_CACHE + targetLoginName)))
                        redisTemplate.delete(Constant.PREFIX_USER_CACHE + targetLoginName);
                }
            }
        }
        return super.put(request, session);
    }

    /**
     * 删除
     */
    @Override
    @PostMapping(value = "/delete",produces = "application/json")
    @ApiOperation(value = "APIJSON:/delete", httpMethod = "POST",notes = "APIJSON接口")
    public String delete(@RequestBody String request, HttpSession session) {
        return super.delete(request, session);
    }

    /**
     *
     * @param request apijson 请求
     * @param httpServletRequest httpServletRequest
     * @param mainName 被限定条件限制的主表名
     * @param departmentClause 传 Constant.Clause_Sign_Bys 或 Constant.Clause_Creator_ids 其中之一,
     *                        注意: departmentClause 一定是 个人权限限定条件之一或 就是 个人权限限定条件;
     *                        即: 若个人限定条件只有一个, 那么 个人权限限定条件为 departmentClause
     * @param isTwoPersonClauseNum 个人数据是添加一个还是两个, false 就是有 1 个限定条件且一定为 departmentClause; true 就是有两个限定条件
     * @param personAnotherClause 另一个 个人限定条件; 如果 isTwoPersonClauseNum 为 false, personAnotherClause设为 null
     * @return
     */
    private String addClause(String request, HttpServletRequest httpServletRequest, String mainName, String departmentClause, boolean isTwoPersonClauseNum, String personAnotherClause) {
        UserDetail subject = getSubject(httpServletRequest);
        JSONObject apiJsonRequest = apijson.JSON.parseObject(request);
        JSONObject firstInner = apiJsonRequest.getJSONObject("[]");
        JSONObject mainTable = firstInner.getJSONObject(mainName);
        // [0d6eb2c04fda4487849955d83e399716, 0d8ba2e6011641e6ad9227c908674616, 4968a62515ab4b43987930c3dda836b2, 66aded05da3011eb89c00242ac140007, 891bc0d8d4b14c9d9f6f8e73bef1732f, db0d5e6ccc554bfa9922b4f8187cd2f2]
        List<String> uIds = userService.getIdByOrganizationId(new UserDto().setOrganizationId(subject.getOrganizationId()));
        if (Objects.equals(Constant.DataRight_Department,subject.getDataRights())){
            if (Objects.equals(Constant.Clause_Sign_By,departmentClause))
                mainTable.put(Constant.Clause_Sign_Bys, uIds);
            if (Objects.equals(Constant.Clause_Creator_Id,departmentClause))
                mainTable.put(Constant.Clause_Creator_Ids, uIds);
            if (Objects.equals(Constant.Clause_Operator_Id,departmentClause))
                mainTable.put(Constant.Clause_Operator_Ids, uIds);
        }
        if (Objects.equals(Constant.DataRight_Personal,subject.getDataRights())) {
            if (isTwoPersonClauseNum){
                mainTable.put(departmentClause, subject.getId());
                mainTable.put(personAnotherClause, subject.getId());
                StringBuffer buffer = new StringBuffer();
                if (mainTable.get(Constant.Clause_Combine) != null)
                    mainTable.put(Constant.Clause_Combine, buffer.append(mainTable.getString(Constant.Clause_Combine)).append(",").append(personAnotherClause).append(",").append(departmentClause));
                else
                    mainTable.put(Constant.Clause_Combine, buffer.append(personAnotherClause).append(",").append(departmentClause));
            }else {
                if (Objects.equals(Constant.Clause_Sign_By,departmentClause))
                    mainTable.put(Constant.Clause_Sign_By, subject.getId());
                if (Objects.equals(Constant.Clause_Creator_Id,departmentClause))
                    mainTable.put(Constant.Clause_Creator_Id, subject.getId());
                if (Objects.equals(Constant.Clause_Operator_Id,departmentClause))
                    mainTable.put(Constant.Clause_Operator_Id, subject.getId());
            }
        }
        return apiJsonRequest.toString();
    }

    private String getToken(HttpServletRequest request) {
        // 获取请求头信息authorization信息
        String authHeader = request.getHeader(JwtUtil.AUTH_HEADER_KEY);
        return authHeader.substring(7);
    }

    /**
     * @return userDetail
     */
    private UserDetail getSubject(HttpServletRequest request) {
        // uid 66aded05da3011eb89c00242ac140007 oid 66aded05da3011eb89c00242ac140020
        String loginName = JwtUtil.getClaim(getToken(request), Constant.LOGIN_NAME);
        if (Boolean.TRUE.equals(redisTemplate.hasKey(Constant.PREFIX_USER_CACHE + loginName)))
            return (UserDetail) redisTemplate.opsForValue().get(Constant.PREFIX_USER_CACHE + loginName);
        return userService.selectOneByUserDetail(new UserDetail().setLoginName(loginName));
    }

    private String getMenuCode(HttpServletRequest request) {
        return request.getHeader(Constant.AUTH_CODE);
    }
}
